package sf.database.mapper;

import sf.database.dao.DBClient;
import sf.database.mapper.annotation.AutoSQL;
import sf.database.mapper.annotation.ExecuteProvider;
import sf.database.mapper.annotation.ExecuteSQL;
import sf.database.mapper.annotation.ExecuteTemplate;
import sf.database.mapper.handle.MethodHandleAutoSQL;
import sf.database.mapper.handle.MethodHandleProvider;
import sf.database.mapper.handle.MethodHandleSQL;
import sf.database.mapper.handle.MethodHandleTemplate;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

/**
 * Java代理实现.
 * @author
 */
public class MapperJavaProxy implements InvocationHandler {

    /**
     * The db client.
     */
    protected DBClient client;

    /**
     * The entity class.
     */
    protected Class<?> entityClass;

    protected DefaultMapperBuilder builder;

    private Class<?> mapperInterface;

    /**
     * 默认的实现
     */
    private DaoMapper<?> defaultDaoMapper;

    /**
     * The Constructor.
     */
    public MapperJavaProxy() {

    }

    /**
     * @param builder
     * @param client
     * @param mapperInterface
     */
    public MapperJavaProxy(DefaultMapperBuilder builder, DBClient client, Class<?> mapperInterface) {
        super();
        this.client = client;
        this.builder = builder;
        this.mapperInterface(mapperInterface);
        this.mapperInterface = mapperInterface;
        this.defaultDaoMapper = new DaoMapperImpl(entityClass, client);
    }


    /**
     * Mapper interface.
     * @param mapperInterface the dao2 interface
     * @return the dao2 proxy
     */
    public MapperJavaProxy mapperInterface(Class<?> mapperInterface) {
        this.onResolveEntityClassFromMapperInterface(mapperInterface);
        return this;
    }


    /**
     * Entity class.
     * @param entityClass the entity class
     * @return the dao2 proxy
     */
    public MapperJavaProxy entityClass(Class<?> entityClass) {
        this.entityClass = entityClass;
        return this;
    }

    /**
     * Check args.
     */
    protected void checkArgs() {
    }

    /**
     * Builds the.
     * @return the dao2 proxy
     */
    public MapperJavaProxy build() {
        this.checkArgs();
        return this;
    }

    /**
     * 获取BaseMapper&lt;EntityClass&gt;接口的泛型实体参数类.
     * @param mapperInterface the dao2 interface
     */
    protected void onResolveEntityClassFromMapperInterface(Class<?> mapperInterface) {
        if (mapperInterface.isInterface()) {
            Type[] faces = mapperInterface.getGenericInterfaces();
            if (faces.length > 0 && faces[0] instanceof ParameterizedType) {
                ParameterizedType pt = (ParameterizedType) faces[0];
                if (pt.getActualTypeArguments().length > 0) {
                    this.entityClass = (Class<?>) pt.getActualTypeArguments()[0];
                }
            }
        } else {
            throw new IllegalArgumentException("mapperInterface is not interface.");
        }
    }


    /**
     * Invoke.
     * @param proxy  the proxy
     * @param method the method
     * @param args   the args
     * @return the object
     * @throws Throwable the throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Class<?> caller = method.getDeclaringClass();
        String methodName = method.getName();
        if ("toString".equals(methodName)) {
            return "Sorm Mapper " + mapperInterface;
        }

        Object o = null;
        //内置的方法，直接调用Invoke
        boolean exist = false;
        if (caller.isAssignableFrom(DaoMapper.class)) {
            o = method.invoke(defaultDaoMapper, args);
            exist = true;
        } else if (method.isDefault()) {//default方法调用
            o = DefaultMethodInvoke.invokeDefaultMethod(proxy, method, args);
            exist = true;
        }

        //解析方法以及注解，找到对应的处理类
        if (!exist) {
            ExecuteTemplate et = method.getAnnotation(ExecuteTemplate.class);
            ExecuteSQL es = method.getAnnotation(ExecuteSQL.class);
            ExecuteProvider ep = method.getAnnotation(ExecuteProvider.class);

            //是否是自动生成sql语句
            boolean autoSql = method.getAnnotation(AutoSQL.class) != null;
            boolean template = et != null;
            boolean sql = es != null;
            boolean provide = ep != null;
            if (template) {
                o = MethodHandleTemplate.getInstance().call(client, entityClass, method, args);
            } else if (sql) {
                o = MethodHandleSQL.getInstance().call(client, entityClass, method, args);
            } else if (autoSql) {
                //自动生成sql模仿spring-data-common
                o = MethodHandleAutoSQL.getInstance().call(client, entityClass, method, args);
            } else if (provide) {
                String sqlTemplate = MethodHandleProvider.getSQLTemplateProvider(ep, method, args);
                o = MethodHandleTemplate.getInstance().call(client, method, sqlTemplate, ep.dmlType(), args);
            }
        }
        return o;
    }

    @Override
    public String toString() {
        return " Proxy";
    }

}
